#include "TaskQueue.h"
#include "ThreadPool.h"

#include <stdlib.h>
#include <ctime>
#include <iostream>
#include <memory>
#include <functional>

using std::cout;
using std::endl;
using std::unique_ptr;
using std::bind;

class MyTask
{
public:
    void process() 
    {
        //实现逻辑该如何写？
        ::srand(::clock());//种随机种子
        int number = ::rand() % 100;
        cout << ">>BO.Threadpool MyTask process number = " << number <<endl;
    }
};

//Q1、任务到底添加了多少次呢？
//A:任务确实添加了20次，因为cnt每次都可以打印20次

//Q2:为何任务没有执行完毕，程序就退出来了？
//原因：主线程在执行20次addTask添加完任务之后，会继续向下执行stop函数，然后子线程
//一直在doTask中执行，先判断while是否可以进去，然后再获取任务与执行任务，如果
//主线程添加完20次任务执行，很快的执行到stop中，将标志位_isExit设置为true，那么
//子线程就不会继续获取任务与执行任务，也就会导致任务执行不完
//
//解决方案:在线程池的stop函数中添加判断条件，只要任务队列不为空，那么就不让不线程
//继续向下执行，while(!_taskQue.empty()),这样可以保证任务执行完毕

//Q3:任务都可以执行完毕了，但是程序又卡住了（无法退出）？
//原因：子线程一直在doTask函数中进行执行，只要标志位_isExit没有设置为true，那么
//多个子线程一直在循环中；对于主线程而言，只要子线程没有执行完任务，那么主线程
//不会将标志位设置为true；
//但是如果子线程在doTask函数中执行getTask函数，将最后几个任务获取完毕之后，那么
//任务队列就为空了，那么如果主线程执行的比较快，那么可以将标志位_isExit设置为true
//并且等着子线程，然后当子线程调用process执行完任务之后，会再走while循环，发现
//标志位_isExit已经设置为true了，那么while就走不进去，子线程执行结束了，那么主线程
//会回收子线程，最终整个程序就结束了（这是一种最理想的情况）
//
//如果子线程在getTask将最后几个任务获取完毕之后，执行的比较快，可以很快的执行完
//process并且走while循环，但是标志位_isExit依旧是false，又进入了while循环,接着
//继续调用getTask时候，发现任务队列为空，就卡主了（在_notEmpty条件变量上睡眠）
//那么即使主线程再将标志位设置为true，也没有效果，最终子线程都在_notEmpty条件
//变量上睡眠
//
//解决方案：如果主线程执行的快，程序是可以完美退出的；但是如果子线程执行的比较快
//最终可能至少有一个子线程在_notEmpty条件变量上睡眠吧，所以可以在主线程执行stop
//的时候，将所有可能在_notEmpty条件变量上的线程全部唤醒;但是唤醒之后，因为
//TaskQueue的pop函数中，_notEmpty.wait函数在while(empty())中，那么此时即使将
//所有的线程唤醒，empty依旧为true，最终为了让工作线程可以完全被唤醒并且可以
//从while循环中跳出来，在增加了一个标志为bool _flag,这样才能达到真正的唤醒并且
//退出来
void test()
{
    unique_ptr<MyTask> ptask(new MyTask());

    ThreadPool pool(4, 10);//创建线程池对象
    pool.start();//启动线程池（启动工作线程）

    //添加任务(添加了20个任务)
    int cnt = 20;
    while(cnt--)
    {
        /* function<void()> f = bind(&MyTask::process, ptask.get()); */
        /* pool.addTask(std::move(f)); */
        pool.addTask(bind(&MyTask::process, ptask.get()));
        cout << "cnt = " << cnt << endl;
    }

    pool.stop();
}

int main(int argc, char *argv[])
{
    test();
    return 0;
}

